package dev.ece.util.jbullet;

import javax.vecmath.Quat4f;
import javax.vecmath.Vector3f;

import com.bulletphysics.collision.broadphase.BroadphaseInterface;
import com.bulletphysics.collision.broadphase.CollisionFilterGroups;
import com.bulletphysics.collision.broadphase.DbvtBroadphase;
import com.bulletphysics.collision.broadphase.Dispatcher;
import com.bulletphysics.collision.dispatch.CollisionConfiguration;
import com.bulletphysics.collision.dispatch.CollisionDispatcher;
import com.bulletphysics.collision.dispatch.CollisionFlags;
import com.bulletphysics.collision.dispatch.CollisionObject;
import com.bulletphysics.collision.dispatch.DefaultCollisionConfiguration;
import com.bulletphysics.collision.dispatch.GhostPairCallback;
import com.bulletphysics.collision.dispatch.PairCachingGhostObject;
import com.bulletphysics.collision.shapes.CollisionShape;
import com.bulletphysics.collision.shapes.ConvexShape;
import com.bulletphysics.dynamics.DiscreteDynamicsWorld;
import com.bulletphysics.dynamics.RigidBody;
import com.bulletphysics.dynamics.character.KinematicCharacterController;
import com.bulletphysics.dynamics.constraintsolver.ConstraintSolver;
import com.bulletphysics.dynamics.constraintsolver.SequentialImpulseConstraintSolver;
import com.bulletphysics.linearmath.DefaultMotionState;
import com.bulletphysics.linearmath.Transform;

public class EceJbulletWorld {

	private CollisionConfiguration configuration;
	
	private Dispatcher dispatcher;
	
	private BroadphaseInterface broadphase;
	
	private ConstraintSolver solver;
	
	private DiscreteDynamicsWorld world;
	
	public EceJbulletWorld() {
		this(new DefaultCollisionConfiguration(), new DbvtBroadphase(), new SequentialImpulseConstraintSolver());
	}
	
	public EceJbulletWorld(CollisionConfiguration configuration, BroadphaseInterface broadphase, ConstraintSolver solver) {
		this.configuration = configuration;
		this.dispatcher = new CollisionDispatcher(configuration);
		this.broadphase = broadphase;
		this.solver = solver;
		this.broadphase.getOverlappingPairCache().setInternalGhostPairCallback(new GhostPairCallback());
		this.world = new DiscreteDynamicsWorld(dispatcher, broadphase, solver, configuration);
	}
	
	public synchronized RigidBody addRigidBody(CollisionShape collisionShape) {
		return addRigidBody(collisionShape, 0);
	}
	
	public synchronized RigidBody addRigidBody(CollisionShape collisionShape, float mass) {
		return addRigidBody(collisionShape, mass, null, null, null);
	}
	
	public synchronized RigidBody addRigidBody(CollisionShape collisionShape, float mass, Vector3f inertia, Vector3f position, Quat4f rotation) {
		RigidBody body = createRigidBody(collisionShape, mass, inertia, position, rotation);
		world.addRigidBody(body);
		return body;
	}
	
	public synchronized RigidBody addRigidBody(RigidBody body) {
		world.addRigidBody(body);
		return body;
	}
	
	public static RigidBody createRigidBody(CollisionShape collisionShape, float mass, Vector3f inertia, Vector3f position, Quat4f rotation) {
		Vector3f localInertia = inertia != null ? inertia: new Vector3f();
		if(mass > 0) {
			collisionShape.calculateLocalInertia(mass, localInertia);
		}
		Transform shapetrans = new Transform();
		shapetrans.setIdentity();
		if(position != null) {
			shapetrans.origin.set(position);
		}
		if(rotation != null) {
			shapetrans.setRotation(rotation);
		}
		DefaultMotionState state = new DefaultMotionState(shapetrans);
		return new RigidBody(mass, state, collisionShape, localInertia);
	}
	
	public synchronized PairCachingGhostObject addGhostObject(ConvexShape shape, float mass, Vector3f inertia, Vector3f position, Quat4f rotation) {
		PairCachingGhostObject obj = createGhostObject(shape, mass, inertia, position, rotation);
		return addGhostObject(obj);
	}
	
	public synchronized PairCachingGhostObject  addGhostObject(PairCachingGhostObject obj) {
		world.addCollisionObject(obj, CollisionFilterGroups.CHARACTER_FILTER, CollisionFilterGroups.ALL_FILTER);
		return obj;
	}
	
	public PairCachingGhostObject createGhostObject(ConvexShape shape, float mass, Vector3f inertia, Vector3f position, Quat4f rotation) {
		Vector3f localInertia = inertia != null ? inertia: new Vector3f();
		if(mass > 0) {
			shape.calculateLocalInertia(mass, localInertia);
		}
		Transform shapetrans = new Transform();
		shapetrans.setIdentity();
		if(position != null) {
			shapetrans.origin.set(position);
		}
		if(rotation != null) {
			shapetrans.setRotation(rotation);
		}
		PairCachingGhostObject obj = new PairCachingGhostObject();
		obj.setCollisionShape(shape);
		obj.setWorldTransform(shapetrans);
		obj.setCollisionFlags(CollisionFlags.CHARACTER_OBJECT);
		return obj;
	}
	
	public synchronized void addKinematicCharacterController(KinematicCharacterController controller) {
		world.addAction(controller);
	}
	
	public synchronized void removeKinematicCharacterController(KinematicCharacterController controller) {
		world.removeAction(controller);
	}
	
	public synchronized void addCollisionObject(CollisionObject collisionObject) {
		world.addCollisionObject(collisionObject);
	}
	
	public synchronized void addCollisionObject(CollisionObject collisionObject, short collisionFilterGroup, short collisionFilterMask) {
		world.addCollisionObject(collisionObject, collisionFilterGroup, collisionFilterMask);
	}
	
	public synchronized void removeCollisionObject(CollisionObject collisionObject) {
		world.removeCollisionObject(collisionObject);
	}
	
	public synchronized void removeRigidBody(RigidBody body) {
		world.removeRigidBody(body);
	}
	
	public synchronized void stepSimulation(float timeStep, int maxSubSteps) {
		//模拟运动
		world.stepSimulation(timeStep, maxSubSteps);
		//碰撞检测 （所有碰撞）
		world.performDiscreteCollisionDetection();
	}

	public void destroy() {
		world.destroy();
	}
	
	public CollisionConfiguration getConfiguration() {
		return configuration;
	}

	public Dispatcher getDispatcher() {
		return dispatcher;
	}

	public BroadphaseInterface getBroadphase() {
		return broadphase;
	}

	public ConstraintSolver getSolver() {
		return solver;
	}

	public DiscreteDynamicsWorld getWorld() {
		return world;
	}
	
}
